package com.nq.activaity.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nq.activaity.entity.StSignInSetting;
import com.nq.activaity.entity.StUserIntegralRecord;
import com.nq.activaity.entity.StUserSignRecord;
import com.nq.activaity.service.IStUserSignRecordService;
import com.nq.common.ServerResponse;
import com.nq.common.SignInTypeEnum;
import com.nq.common.UserIntegralTypeEnum;
import com.nq.dao.UserCashDetailMapper;
import com.nq.dao.UserMapper;
import com.nq.dao.activaity.StSignInSettingMapper;
import com.nq.dao.activaity.StUserIntegralRecordMapper;
import com.nq.dao.activaity.StUserSignRecordMapper;
import com.nq.pojo.User;
import com.nq.pojo.UserCashDetail;
import com.nq.service.IUserService;
import com.nq.utils.DateTimeUtil;
import com.nq.utils.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Description: 用户签到表
 * @Author: jeecg-boot
 * @Date:   2024-11-06
 * @Version: V1.0
 */
@Service
public class StUserSignRecordServiceImpl extends ServiceImpl<StUserSignRecordMapper, StUserSignRecord> implements IStUserSignRecordService {


    @Resource
    private IUserService userService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private StUserSignRecordMapper userSignRecordMapper;

    @Resource
    private StSignInSettingMapper signInSettingMapper;

    @Resource
    private UserCashDetailMapper userCashDetailMapper;

    @Resource
    private StUserIntegralRecordMapper userIntegralRecordMapper;


    /**
     * 签到列表
     * @param month
     * @param request
     * @return
     */
    @Override
    public ServerResponse list(String month, HttpServletRequest request) {
        User user = this.userService.getCurrentRefreshUser(request);
        if (user == null) {
            return ServerResponse.createByErrorMsg("The user does not exist");
        }
        if (StringUtils.isBlank(month)) {
            month = DateUtils.date2Str(DateUtils.yyyyMM.get());
        }
        LambdaQueryWrapper<StUserSignRecord> recordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        recordLambdaQueryWrapper.eq(StUserSignRecord::getUserId, user.getId())
                .likeRight(StUserSignRecord::getSignInDate, month)
                .orderByDesc(StUserSignRecord::getCreateTime);
        List<StUserSignRecord> recordList = userSignRecordMapper.selectList(recordLambdaQueryWrapper);
        return ServerResponse.createBySuccess(recordList);
    }


    /**
     * 签到
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServerResponse sign(HttpServletRequest request, String date) {
        User user = this.userService.getCurrentRefreshUser(request);
        if (user == null) {
            return ServerResponse.createByErrorMsg("The user does not exist.");
        }
        // 获取总积分
        AtomicReference<Integer> totalIntegral = new AtomicReference<>(0);
        Map<String, Object> map = new HashMap<>();
        map.put("totalIntegral", 0);
        map.put("totalMoney", 0);
        synchronized (user.getId()) {
            try {
                String todayDate = DateUtils.formatDate(Calendar.getInstance().getTime(), "yyyy-MM-dd");
                if (!date.equals(todayDate)) {
                    return ServerResponse.createByErrorMsg("The sign-in date is not the same day.");
                }

                if (hasSignedInToday(user.getId(), todayDate)) {
                    return ServerResponse.createByErrorMsg("Have signed in today.");
                }

                // 插入签到记录
                Integer signRecordId = insertSignInRecord(user.getId());

                // 获取奖励
                List<StSignInSetting> signInSettings = getSignInSettings();
                signInSettings.forEach(setting -> {
                    SignInTypeEnum signInType = SignInTypeEnum.getNameFromValue(setting.getSignInType());
                    if (signInType != null) {
                        signInRewards(user.getId(), setting, signInType, map);
                    }
                });

                // 修改签到表的积分奖励和余额奖励
                updateSignRecord(map, signRecordId);

            } catch (Exception e) {
                log.error("签到失败！" + e.getMessage());
                return ServerResponse.createByErrorMsg("Sign-in exception.");
            }
        }
        return ServerResponse.createBySuccess(totalIntegral.get());
    }

    /**
     * 提示文案
     * @param request
     * @return
     */
    @Override
    public ServerResponse tips(HttpServletRequest request) {
        User user = this.userService.getCurrentRefreshUser(request);
        if (user == null) {
            return ServerResponse.createByErrorMsg("The user does not exist.");
        }
        // 获取签到配置
        LambdaQueryWrapper<StSignInSetting> settingLambdaQueryWrapper = new LambdaQueryWrapper<>();
        settingLambdaQueryWrapper.eq(StSignInSetting::getSignInType, SignInTypeEnum.CONTINUOUS.getType());
        List<StSignInSetting> signInSettings = signInSettingMapper.selectList(settingLambdaQueryWrapper);
        // 连续签到：计算连续签到天数
        List<StUserSignRecord> signRecordList = getSignInRecords(user.getId());
        // 使用 reduce 来找到最小剩余天数和对应的连续签到天数
        Optional<AbstractMap.SimpleEntry<Integer, Integer>> minLeftDaysEntry = signInSettings.stream()
                .map(setting -> {
                    // 连续签到天数
                    int consecutiveDays = newCalculateConsecutiveDays(signRecordList, false);
                    // 剩余天数
                    int leftDays = setting.getSignInDays().intValue() - consecutiveDays;
                    // 返回一个 Map.Entry 对象，包含剩余天数和对应的连续签到天数
                    return new AbstractMap.SimpleEntry<>(leftDays, consecutiveDays);
                })
                .reduce((entry1, entry2) -> entry1.getKey() < entry2.getKey() ? entry1 : entry2);

        Map<String, Integer> map = new HashMap<>();
        // 如果找到最小剩余天数的记录，进行处理
        minLeftDaysEntry.ifPresent(entry -> {
            // 最小剩余天数
            int minLeftDays = entry.getKey();
            // 对应的连续签到天数
            int correspondingConsecutiveDays = entry.getValue();
            map.put("leftDays", minLeftDays);
            map.put("consecutiveDays", correspondingConsecutiveDays);
        });
        return ServerResponse.createBySuccess(map);
    }

    /**
     * 修改签到表的积分奖励和余额奖励
     * @param map
     * @param signRecordId
     */
    private void updateSignRecord(Map<String, Object> map, Integer signRecordId) {
        StUserSignRecord signRecord = new StUserSignRecord();
        signRecord.setId(signRecordId);
        signRecord.setRewardPoints(Integer.parseInt(map.get("totalIntegral").toString()));
        signRecord.setRewardMount(new BigDecimal(map.get("totalMoney").toString()));
        userSignRecordMapper.updateById(signRecord);
    }

    /**
     * 判断今天是否签到
     * @param userId
     * @param todayDate
     * @return
     */
    private boolean hasSignedInToday(Integer userId, String todayDate) {
        LambdaQueryWrapper<StUserSignRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StUserSignRecord::getUserId, userId).eq(StUserSignRecord::getSignInDate, todayDate);
        return userSignRecordMapper.selectOne(queryWrapper) != null;
    }

    /**
     * 插入签到记录
     * @param userId
     */
    private Integer insertSignInRecord(Integer userId) {
        StUserSignRecord signRecord = new StUserSignRecord();
        signRecord.setUserId(userId);
        signRecord.setSignInDate(new Date());
        signRecord.setCreateTime(new Date());
        signRecord.setUpdateTime(new Date());
        signRecord.setRewardMount(BigDecimal.ZERO);
        signRecord.setRewardPoints(0);
        userSignRecordMapper.insert(signRecord);
        return signRecord.getId();
    }

    /**
     * 获取签到配置
     * @return
     */
    private List<StSignInSetting> getSignInSettings() {
        return signInSettingMapper.selectList(new QueryWrapper<>());
    }

    /**
     * 用户签到（包括普通签到、连续签到、累计签到）
     * @param userId 用户ID
     * @param setting 签到设置
     * @param signInType 签到类型
     */
    private void signInRewards(Integer userId, StSignInSetting setting, SignInTypeEnum signInType, Map<String, Object> map) {
        // 只需查询一次用户信息和签到记录
        User user = userMapper.selectById(userId);
        long signInRecordCount = getSignInRecordCount(userId);

        // 签到条件是否满足
        boolean signInConditionMet = checkSignInCondition(userId, setting, signInType, signInRecordCount);

        if (signInConditionMet) {
            // 积分累加
            map.put("totalIntegral", Integer.parseInt(map.get("totalIntegral").toString()) + setting.getSignInPoints());
            // 金额累加
            map.put("totalMoney", new BigDecimal(map.get("totalMoney").toString()).add(setting.getSignInAmount()));
            // 执行签到奖励
            executeSignInRewards(user, setting, signInType);
        }
    }


    /**
     * 签到条件判断
     * @param userId
     * @param setting
     * @param signInType
     * @param signInRecordCount
     * @return
     */
    private boolean checkSignInCondition(Integer userId, StSignInSetting setting, SignInTypeEnum signInType, long signInRecordCount) {
        boolean conditionMet = false;

        switch (signInType) {
            case ORDINARY:
                // 普通签到：比较签到天数
                conditionMet = signInRecordCount == setting.getSignInDays().longValue();
                break;
            case CONTINUOUS:
                // 连续签到：计算连续签到天数
                List<StUserSignRecord> signRecordList = getSignInRecords(userId);
                int consecutiveDays = newCalculateConsecutiveDays(signRecordList, true);
                conditionMet = (calculateValue(consecutiveDays, setting.getSignInDays()) == setting.getSignInDays().intValue());
                break;
            case ACCUMULATE:
                // 累加签到：直接比较总签到次数
                conditionMet = (signInRecordCount == setting.getSignInDays().longValue());
                break;
        }

        return conditionMet;
    }


    /**
     * 执行签到奖励操作
     * @param user
     * @param setting
     * @param signInType
     */
    private void executeSignInRewards(User user, StSignInSetting setting, SignInTypeEnum signInType) {
        // 更新用户余额
        updateUserBalance(user, setting.getSignInAmount());

        // 插入流水表
        insertUserCashDetail(user, setting, signInType);

        // 赠送积分
        userService.updateUserIntegral(user.getId(), setting.getSignInPoints(), getIntegralTypeDesc(setting), true, setting.getId());

    }

    /**
     * 获取用户签到记录数
     * @param userId 用户ID
     * @return 签到记录数
     */
    private long getSignInRecordCount(Integer userId) {
        LambdaQueryWrapper<StUserSignRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StUserSignRecord::getUserId, userId)
                .likeRight(StUserSignRecord::getSignInDate, DateUtils.date2Str(DateUtils.yyyyMM.get()));
        return userSignRecordMapper.selectCount(queryWrapper);
    }

    /**
     * 获取用户签到记录列表（连续签到专用）
     * @param userId 用户ID
     * @return 签到记录列表
     */
    private List<StUserSignRecord> getSignInRecords(Integer userId) {
        LambdaQueryWrapper<StUserSignRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StUserSignRecord::getUserId, userId)
                .orderByDesc(StUserSignRecord::getSignInDate);
        return userSignRecordMapper.selectList(queryWrapper);
    }

    /**
     * 更新用户余额
     * @param user 用户对象
     * @param amount 更新金额
     */
    private void updateUserBalance(User user, BigDecimal amount) {
        user.setEnableAmt(user.getEnableAmt().add(amount));
        userMapper.updateById(user);
    }

    /**
     * 插入用户现金流水记录
     * @param user 用户对象
     * @param setting 签到设置
     * @param signInType 签到类型
     */
    private void insertUserCashDetail(User user, StSignInSetting setting, SignInTypeEnum signInType) {
        UserCashDetail ucd = new UserCashDetail();
        ucd.setAgentId(user.getAgentId());
        ucd.setAgentName(user.getAgentName());
        ucd.setUserId(user.getId());
        ucd.setUserName(user.getRealName());
        ucd.setDeType(signInType.name());
        ucd.setDeSummary(getSignInSummary(signInType, setting));
        ucd.setDeAmt(setting.getSignInAmount());
        ucd.setAddTime(new Date());
        ucd.setIsRead(0);
        userCashDetailMapper.insert(ucd);
    }

    /**
     * 获取签到类型对应的总结描述
     * @param signInType 签到类型
     * @param setting 签到设置
     * @return 签到总结
     */
    private String getSignInSummary(SignInTypeEnum signInType, StSignInSetting setting) {
        switch (signInType) {
            case ORDINARY:
                return "ordinary sign in";
            case CONTINUOUS:
                return setting.getSignInDays() + " consecutive days to obtain.";
            case ACCUMULATE:
                return "accumulate sign in";
            default:
                return "unknown sign in type";
        }
    }


    /**
     * 获取积分类型描述
     * @param setting 签到设置
     * @return 积分类型描述
     */
    private String getIntegralTypeDesc(StSignInSetting setting) {
        if (setting.getSignInType().equals(SignInTypeEnum.ORDINARY.getType())) {
            return UserIntegralTypeEnum.ORDINARY_SIGN_IN.getDesc();
        } else if (setting.getSignInType().equals(SignInTypeEnum.CONTINUOUS.getType())) {
            return UserIntegralTypeEnum.CONTINUOUS_SIGN_IN.getDesc();
        } else {
            return UserIntegralTypeEnum.ACCUMULATE_SIGN_IN.getDesc();
        }
    }



    /**
     * 计算连续签到的天数
     */
    private int newCalculateConsecutiveDays (List<StUserSignRecord> signList, boolean isSign) {
        int consecutiveDays = 0;
        int currentStreak = 0;
        Date currentDate = new Date();

        for (StUserSignRecord sign : signList) {
            // 计算当前记录距离今天的天数
            long daysDiff = daysBetween(sign.getSignInDate(), currentDate);

            if (isSign) {
                if (daysDiff == currentStreak) {
                    // 连续签到
                    currentStreak++;
                } else {
                    // 重置连续签到计数
                    currentStreak = 0;
                }

                if (currentStreak > consecutiveDays) {
                    consecutiveDays = currentStreak;
                }
            }else {
                if (daysDiff == currentStreak || (daysDiff == currentStreak + 1)) {
                    // 连续签到
                    currentStreak++;
                } else {
                    // 重置连续签到计数
                    currentStreak = 0;
                }

                if (currentStreak > consecutiveDays) {
                    consecutiveDays = currentStreak;
                }
            }
        }

        return consecutiveDays;
    }



    public int calculateValue(int input, int day) {
        if (input <= day) {
            // 如果小于30，返回原值
            return input;
        } else {
            // 计算减去30的倍数后的值
            int remainder = input % day;
            return remainder;
        }
    }


    private long daysBetween (Date date1, Date date2) {
        long diffInMilliseconds = date2.getTime () - date1.getTime ();
        return TimeUnit.DAYS.convert (diffInMilliseconds, TimeUnit.MILLISECONDS);
    }



}
